﻿using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using RgCore.Database;
using RgCore.GameData;
using RgCore.Menu;
using RgCore.PlayerHud;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace TaskSystem
{
    public static class TaskExtensions
    {
        public static void Achieve(this Player player, ref Task task)
        {
            task.Process++;
            if (task.IsCompleted)
            {
                Complete(player, ref task);
            }
        }

        public static void Complete(this Player player, ref Task task)
        {
            var gd = GameplayData.GameDatas.FirstOrDefault(x => x.Player == player);
            if (gd != null)
            {
                gd.AddExp(task.Exp_Reward, true);
                gd.AddPoints(task.Points_Reward, true);
                string type = task.IsWeekly ? "<color=#48D1CC>每周挑战</color>" : "<color=#FF8247>每日任务</color>";
                player.SendFormattedMsg("finish", "<color=#00FF00>任务已完成</color>", $"[{type}] <color=yellow>{task.Name}</color> 已完成！", 5, MsgType.Success_Response, ScreenLocation.CenterTop, false);
            }
        }
    }

    public class TaskModule
    {
        public TaskModule(Player player)
        {
            Player = player;
        }

        public Player Player { get; }
        public List<Task> TaskList = new List<Task>();

        public async void InitializeTaskAsync()
        {
            try
            {
                if (DatabaseController.DatabaseDisabled)
                {
                    return;
                }
                DateTime lastLogin = GameplayData.GameDatas.FirstOrDefault(x => x.Player == Player).LastLogin;
                bool signedtoday = lastLogin.Date >= DateTime.Now.Date;
                bool signedthisweek = false;
                DateTime d = DateTime.Now.Date;
                while (d.DayOfWeek != DayOfWeek.Monday)
                    d = d.AddDays(-1);
                signedthisweek = lastLogin.Date >= d;
                Dictionary<string, object> filter = new Dictionary<string, object> { { "userid", Player.UserId } };
                QueryCommandRequest task = new QueryCommandRequest(RgCore.RgCore.instance.Config.Database.PlayerTable, filter,
                        false, "daily_task", "weekly_task");
                var result = await task.GetResponseResult();
                string origin_daily = result.First()["daily_task"].ToString();
                string origin_weekly = result.First()["weekly_task"].ToString();
                if (!signedtoday)
                {
                    RefreshDailyTask();
                    Player.SendFormattedMsg("dailytask", "<color=#FF8247>每日任务</color>", "每日任务已更新！打开菜单看看吧！", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    var ava = TaskHandler.AvailableTasks.Where(x => !x.IsWeekly).ToList();
                    string[] dailytasks = origin_daily.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    if (dailytasks.Length == 0)
                    {
                        RefreshDailyTask();
                    }
                    else
                    {
                        foreach (string str in dailytasks)
                        {
                            string[] array = str.Split(new char[] { '|' });
                            int index = ava.FindIndex(x => x.Id == array[0]);
                            if (index != -1)
                            {
                                var x = ava[index];
                                x.Process = uint.Parse(array[1]);
                                AddTask(x);
                            }
                        }
                    }
                }
                if (!signedthisweek)
                {
                    RefreshWeeklyTask();
                    Player.SendFormattedMsg("weeklytask", "<color=#48D1CC>每周挑战</color>", "每周挑战已更新！打开菜单看看吧！", 5, MsgType.Notification, ScreenLocation.CenterTop, false);
                }
                else
                {
                    var ava = TaskHandler.AvailableTasks.Where(x => x.IsWeekly).ToList();
                    string[] weeklytasks = origin_weekly.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    if (weeklytasks.Length == 0)
                    {
                        RefreshWeeklyTask();
                    }
                    else
                    {
                        foreach (string str in weeklytasks)
                        {
                            string[] array = str.Split(new char[] { '|' });
                            int index = ava.FindIndex(x => x.Id == array[0]);
                            if (index != -1)
                            {
                                var x = ava[index];
                                x.Process = uint.Parse(array[1]);
                                AddTask(x);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error(e);
            }
        }

        public async void RefreshDailyTask()
        {
            await System.Threading.Tasks.Task.Run(() =>
            {
                var ava = TaskHandler.AvailableTasks.Where(x => !x.IsWeekly).ToList();
                for (int i = 0; i < 3 && ava.Count > 0; i++)
                {
                    var tar = ava[UnityEngine.Random.Range(0, ava.Count)];
                    ava.Remove(tar);
                    AddTask(tar);
                }
            });
        }

        public async void RefreshWeeklyTask()
        {
            await System.Threading.Tasks.Task.Run(() =>
            {
                var ava = TaskHandler.AvailableTasks.Where(x => x.IsWeekly).ToList();
                for (int i = 0; i < 7 && ava.Count > 0; i++)
                {
                    var tar = ava[UnityEngine.Random.Range(0, ava.Count)];
                    ava.Remove(tar);
                    AddTask(tar);
                }
            });
        }

        public void AddTask(Task task)
        {
            if (!TaskList.Any(x => x.Id == task.Id))
            {
                TaskList.Add(task);
            }
        }

        public void RemoveTask(string taskid)
        {
            var index = TaskList.FindIndex(x => x.Id == taskid);
            if (index != -1)
            {
                var task = TaskList[index];
                TaskList.RemoveAt(index);
            }
        }
    }

    public static class TaskHandler
    {
        /// <summary>
        /// 每个任务的订阅列表 Key: 事件参数名 Value: 所有订阅该事件的任务ID集合
        /// </summary>
        public static Dictionary<string, List<string>> SubscribeList = new Dictionary<string, List<string>>();

        public static List<Task> AvailableTasks = new List<Task>();
        public static List<TaskModule> TaskModules = new List<TaskModule>();

        public static Page RootPage = null;

        public static Page DailyPage = null;

        public static Page WeeklyPage = null;

        public static void LoadSubscribe()
        {
            foreach (var field in typeof(Tasks).GetFields().Where(x => x.FieldType == typeof(Task)))
            {
                var attr = field.GetCustomAttribute<EventSubscriberAttribute>();
                if (attr != null)
                {
                    foreach (var name in attr.ArgName)
                    {
                        if (!SubscribeList.ContainsKey(name))
                        {
                            SubscribeList.Add(name, new List<string>());
                        }
                        SubscribeList[name].Add(field.Name);
                    }
                }
            }
        }

        public static void SetEnabled(bool enabled)
        {
            if (enabled)
            {
                LoadSubscribe();
                RootPage = new Page(MenuInterface.Root, "Tasks", "游戏任务", null, null, null, false);
                RootPage.Children = (player) =>
                {
                    var tasklist = TaskModules.FirstOrDefault(x => x.Player == player).TaskList;
                    DailyPage = new Page(RootPage, "DailyTasks", "每日任务", (p) =>
                    {
                        int count = tasklist.Count(x => !x.IsWeekly && !x.IsCompleted);
                        string status = count == 0 ? "<color=#00FF00>已全部完成</color>" : $"剩余 <color=yellow>{count}</color> 个";
                        return $"每日任务（{status}）";
                    }, null, MenuDailyTask, false);
                    WeeklyPage = new Page(RootPage, "WeeklyTasks", "每周任务", (p) =>
                    {
                        int count = tasklist.Count(x => x.IsWeekly && !x.IsCompleted);
                        string status = count == 0 ? "<color=#00FF00>已全部完成</color>" : $"剩余 <color=yellow>{count}</color> 个";
                        return $"每周任务（{status}）";
                    }, null, MenuWeeklyTask, false);
                    return new MenuElement[] { DailyPage, WeeklyPage };
                };
                MenuInterface.RegisteredElements.Add(RootPage);
                foreach (var field in typeof(Tasks).GetFields().Where(x => x.FieldType == typeof(Task)))
                {
                    AvailableTasks.Add((Task)field.GetValue(null));
                }
                RgCore.GameData.GameplayData.OnInitializingData += GameplayData_OnInitializingData;
                RgCore.GameData.GameplayData.OnUploadingData += GameplayData_OnUploadingData;
                Exiled.Events.Handlers.Server.RoundEnded += EventCatcher;
                Exiled.Events.Handlers.Player.Died += EventCatcher;
                Exiled.Events.Handlers.Player.UsedItem += EventCatcher;
                Exiled.Events.Handlers.Player.Dying += EventCatcher;
                Exiled.Events.Handlers.Player.Escaping += EventCatcher;
                Exiled.Events.Handlers.Player.EscapingPocketDimension += EventCatcher;
                Exiled.Events.Handlers.Scp079.GainingLevel += EventCatcher;
                Exiled.Events.Handlers.Scp049.SendingCall += EventCatcher;
                Exiled.Events.Handlers.Player.Jumping += EventCatcher;
            }
            else
            {
                MenuInterface.RegisteredElements.Remove(RootPage);
                RootPage = null;
                RgCore.GameData.GameplayData.OnInitializingData -= GameplayData_OnInitializingData;
                RgCore.GameData.GameplayData.OnUploadingData -= GameplayData_OnUploadingData;
                Exiled.Events.Handlers.Server.RoundEnded -= EventCatcher;
                Exiled.Events.Handlers.Player.Died -= EventCatcher;
                Exiled.Events.Handlers.Player.UsedItem -= EventCatcher;
                Exiled.Events.Handlers.Player.Dying -= EventCatcher;
                Exiled.Events.Handlers.Player.Escaping -= EventCatcher;
                Exiled.Events.Handlers.Player.EscapingPocketDimension -= EventCatcher;
                Exiled.Events.Handlers.Scp079.GainingLevel -= EventCatcher;
                Exiled.Events.Handlers.Scp049.SendingCall -= EventCatcher;
                Exiled.Events.Handlers.Player.Jumping -= EventCatcher;
            }
        }

        private static MenuElement[] MenuWeeklyTask(Player player)
        {
            List<MenuElement> eles = new List<MenuElement>();
            var tasklist = TaskModules.FirstOrDefault(x => x.Player == player).TaskList.Where(x => x.IsWeekly);
            foreach (var task in tasklist)
            {
                string color = task.IsCompleted ? "#00FF00" : "yellow";
                eles.Add(new Page(WeeklyPage, $"Task_{task.Id}", $"-[<color=#88FFCC>{task.Name}</color> (<color={color}>{task.Process}/{task.Target}</color>)", null, null, (p) =>
                    new MenuElement[]
                    {
                    new Text(WeeklyPage, "Description", () => $"<color=#00FF00>任务描述</color>: {task.Description}", false),
                    new Text(WeeklyPage, "ExpReward", () => $"<color=#77FF77>经验奖励</color>: {task.Exp_Reward}", false),
                    new Text(WeeklyPage, "PointsReward", () => $"<color=#FFD700>积分奖励</color>: {task.Points_Reward}", false)
                    }, false));
            }
            return eles.ToArray();
        }

        private static MenuElement[] MenuDailyTask(Player player)
        {
            List<MenuElement> eles = new List<MenuElement>();
            var tasklist = TaskModules.FirstOrDefault(x => x.Player == player).TaskList.Where(x => !x.IsWeekly);
            foreach (var task in tasklist)
            {
                string color = task.IsCompleted ? "#00FF00" : "yellow";
                eles.Add(new Page(DailyPage, $"Task_{task.Id}", $"-[<color=#88FFCC>{task.Name}</color> (<color={color}>{task.Process}/{task.Target}</color>)", null, null, (p) =>
                    new MenuElement[]
                    {
                    new Text(DailyPage, "Description", () => $"<color=#00FF00>任务描述</color>: {task.Description}", false),
                    new Text(DailyPage, "ExpReward", () => $"<color=#77FF77>经验奖励</color>: {task.Exp_Reward}", false),
                    new Text(DailyPage, "PointsReward", () => $"<color=#FFD700>积分奖励</color>: {task.Points_Reward}", false)
                    }, false));
            }
            return eles.ToArray();
        }

        private static async void GameplayData_OnUploadingData(Player obj)
        {
            if (DatabaseController.DatabaseDisabled)
                return;
            string daily = "";
            string weekly = "";
            TaskModule tm = TaskModules.FirstOrDefault(x => x.Player == obj);
            if (tm != null)
            {
                foreach (var task in tm.TaskList)
                {
                    string builder = $"{task.Id}|{task.Process};";
                    if (task.IsWeekly)
                        weekly += builder;
                    else
                        daily += builder;
                }
                NoResponseCommandRequest cmd = new NoResponseCommandRequest(RgCore.RgCore.instance.Config.Database.PlayerTable,
                    RequestType.Update, new Dictionary<string, object>
                    { { "daily_task", daily }, { "weekly_task", weekly } }, new Dictionary<string, object>
                    {
                    {"userid", obj.UserId }
                    });
                await cmd.ProcessNonQuery();
                TaskModules.Remove(tm);
            }
        }

        private static void EventCatcher(IExiledEvent ev)
        {
            if (SubscribeList.TryGetValue(ev.GetType().Name, out var list))
            {
                foreach (Player player in Player.List)
                {
                    var tm = TaskModules.FirstOrDefault(x => x.Player == player);
                    if (tm != null)
                    {
                        for (int i = 0; i < tm.TaskList.Count; i++)
                        {
                            var task = tm.TaskList[i];
                            if (!task.IsCompleted && list.Contains(task.Id) && task.Checker(ev, player))
                            {
                                player.Achieve(ref task);
                                tm.TaskList[i] = task;
                            }
                        }
                    }
                }
            }
        }

        private static void GameplayData_OnInitializingData(Player player)
        {
            var tm = new TaskModule(player);
            TaskModules.Add(tm);
            tm.InitializeTaskAsync();
        }
    }
}